package store

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"gin-epg/internal/app/dao"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

// OSSStoreService OSS 存储服务结构体
type OSSStoreService struct {
	client *oss.Client
	bucket *oss.Bucket
}

// NewOSSStoreService 创建新的 OSS 存储服务实例
func NewOSSStoreService() (*OSSStoreService, error) {
	if dao.Client == nil || dao.Bucket == "" {
		dao.Logger.Error("OSS 客户端或存储桶未初始化")
		return nil, fmt.Errorf("OSS 客户端或存储桶未初始化")
	}
	bucket, err := dao.Client.Bucket(dao.Bucket)
	if err != nil {
		dao.Logger.Errorf("创建 OSS 存储服务实例时出错: %v", err)
		return nil, err
	}
	return &OSSStoreService{
		client: dao.Client,
		bucket: bucket,
	}, nil
}

// Save 保存数据到 OSS
func (s *OSSStoreService) Save(key string, data []byte) error {
	reader := bytes.NewReader(data)
	return s.bucket.PutObject(key, reader)
}

// SaveString 保存字符串数据到 OSS
func (s *OSSStoreService) SaveString(key string, data string) error {
	reader := bytes.NewReader([]byte(data))
	return s.bucket.PutObject(key, reader)
}

// GetFileStream 获取文件流
func (s *OSSStoreService) GetFileStream(key string) (io.ReadSeeker, error) {
	object, err := s.bucket.GetObject(key)
	if err != nil {
		return nil, err
	}
	defer object.Close()

	// 将对象内容读取到 bytes.Buffer 中
	var buffer bytes.Buffer
	if _, err := io.Copy(&buffer, object); err != nil {
		return nil, err
	}

	// 使用 bytes.NewReader 创建一个支持 Seek 的 Reader
	return bytes.NewReader(buffer.Bytes()), nil
}

// GetFileInfo 获取文件信息
// GetFileInfo 获取文件信息
func (s *OSSStoreService) GetFileInfo(key string) (os.FileInfo, error) {
	objectStat, err := s.bucket.GetObjectDetailedMeta(key)
	if err != nil {
		dao.Logger.Errorf("获取文件信息时出错: %v", err)
		return nil, err
	}

	// 获取Content-Length和Last-Modified从响应头
	contentLength := objectStat.Get("Content-Length")
	lastModifiedStr := objectStat.Get("Last-Modified")

	if contentLength == "" || lastModifiedStr == "" {
		dao.Logger.Error("无法获取文件信息")
		return nil, fmt.Errorf("无法获取文件信息")
	}

	size, err := strconv.ParseInt(contentLength, 10, 64)
	if err != nil {
		dao.Logger.Errorf("解析文件大小时出错: %v", err)
		return nil, err
	}

	modTime, err := time.Parse(time.RFC1123, lastModifiedStr)
	if err != nil {
		dao.Logger.Errorf("解析文件修改时间时出错: %v", err)
		return nil, err
	}

	// 构造一个os.FileInfo对象
	fileInfo := &fileInfo{
		name:    key,
		size:    size,
		modTime: modTime,
	}
	return fileInfo, nil
}

// fileInfo 实现os.FileInfo接口
type fileInfo struct {
	name    string
	size    int64
	modTime time.Time
}

func (fi *fileInfo) Name() string {
	return fi.name
}

func (fi *fileInfo) Size() int64 {
	return fi.size
}

func (fi *fileInfo) Mode() os.FileMode {
	return os.ModePerm
}

func (fi *fileInfo) ModTime() time.Time {
	return fi.modTime
}

func (fi *fileInfo) IsDir() bool {
	return false
}

func (fi *fileInfo) Sys() interface{} {
	return nil
}

// SaveKeyGz 压缩并保存数据到 OSS
func (s *OSSStoreService) SaveKeyGz(key string) error {
	// 定义本地临时目录
	tempDir := os.TempDir()
	// 定义本地未压缩文件路径
	localFilePath := filepath.Join(tempDir, key)
	// 定义本地压缩文件路径
	compressedFilePath := localFilePath + ".gz"

	// 从 OSS 下载文件到本地临时目录
	if err := s.DownloadToFile(key, localFilePath); err != nil {
		dao.Logger.Errorf("failed to download file: %v", err)
		return fmt.Errorf("failed to download file: %v", err)
	}

	// 压缩文件
	if err := s.compressFile(localFilePath, compressedFilePath); err != nil {
		dao.Logger.Errorf("failed to compress file: %v", err)
		return fmt.Errorf("failed to compress file: %v", err)
	}

	// 上传压缩后的文件到 OSS
	if err := s.uploadFileToOSS(compressedFilePath, key+".gz"); err != nil {
		dao.Logger.Errorf("failed to upload compressed file: %v", err)
		return fmt.Errorf("failed to upload compressed file: %v", err)
	}

	// 清理本地临时文件
	if err := s.cleanupFiles([]string{localFilePath, compressedFilePath}); err != nil {
		dao.Logger.Errorf("failed to clean up files: %v", err)
		return fmt.Errorf("failed to clean up files: %v", err)
	}

	// 替换 fmt.Printf 为 dao.Logger.Info
	dao.Logger.Infof("File %s has been compressed and uploaded to OSS as %s.gz", key, key)
	return nil
}

// ListDirectory 列出指定前缀下的文件和文件夹
func (s *OSSStoreService) ListDirectory(prefix string) ([]FileInfo, error) {
	var result []FileInfo
	marker := ""

	for {
		// 列出指定前缀下的所有对象
		objects, err := s.bucket.ListObjects(oss.Prefix(prefix), oss.Marker(marker), oss.MaxKeys(1000))
		if err != nil {
			return nil, err
		}

		// 处理文件夹
		for _, commonPrefix := range objects.CommonPrefixes {
			result = append(result, FileInfo{
				Name:  commonPrefix,
				IsDir: true,
			})
		}

		// 处理文件
		for _, object := range objects.Objects {
			if strings.HasPrefix(object.Key, prefix) && !strings.HasSuffix(object.Key, "/") {
				result = append(result, FileInfo{
					Name:  object.Key[len(prefix):],
					IsDir: false,
				})
			}
		}

		// 检查是否还有更多对象
		if len(objects.Objects) < 1000 {
			break
		}

		// 更新 marker 以获取下一页
		marker = objects.NextMarker
	}

	return result, nil
}

// downloadToFile 从 OSS 下载文件到本地
func (s *OSSStoreService) DownloadToFile(key, localFilePath string) error {
	file, err := os.Create(localFilePath)
	if err != nil {
		dao.Logger.Errorf("创建本地文件时出错: %v", err)
		return err
	}
	defer file.Close()

	object, err := s.bucket.GetObject(key)
	if err != nil {
		dao.Logger.Errorf("从 OSS 获取对象时出错: %v", err)
		return err
	}
	defer object.Close()

	_, err = io.Copy(file, object)
	if err != nil {
		dao.Logger.Errorf("复制对象到本地文件时出错: %v", err)
		return err
	}
	return nil
}

// compressFile 压缩文件
func (s *OSSStoreService) compressFile(src, dst string) error {
	srcFile, err := os.Open(src)
	if err != nil {
		dao.Logger.Errorf("打开源文件时出错: %v", err)
		return err
	}
	defer srcFile.Close()

	dstFile, err := os.Create(dst)
	if err != nil {
		dao.Logger.Errorf("创建目标文件时出错: %v", err)
		return err
	}
	defer dstFile.Close()

	gz := gzip.NewWriter(dstFile)
	defer gz.Close()

	_, err = io.Copy(gz, srcFile)
	if err != nil {
		dao.Logger.Errorf("压缩文件时出错: %v", err)
		return err
	}
	return nil
}

// uploadFileToOSS 上传文件到 OSS
func (s *OSSStoreService) uploadFileToOSS(localFilePath, key string) error {
	file, err := os.Open(localFilePath)
	if err != nil {
		dao.Logger.Errorf("打开本地文件时出错: %v", err)
		return err
	}
	defer file.Close()

	err = s.bucket.PutObject(key, file)
	if err != nil {
		dao.Logger.Errorf("上传文件到 OSS 时出错: %v", err)
		return err
	}
	return nil
}

// cleanupFiles 清理本地临时文件
func (s *OSSStoreService) cleanupFiles(files []string) error {
	for _, file := range files {
		if err := os.Remove(file); err != nil {
			dao.Logger.Errorf("删除文件时出错: %v", err)
			return err
		}
	}
	return nil
}
